/*
 * XInput1_3-dev - Replacement of xinput library to help with creating
 *                 drivers/debugging application
 *
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.
 */

#include "stdafx.h"

#include "DeviceInfo.h"
#include "DeviceList.h"
#include "DriverComm.h"
#include "Protocol.h"
#include "Utilities.h"

static DWORD g_dwDeviceListSize = NULL;
static PDEVICE_INFO *g_pDeviceList = NULL;

HRESULT GrowList(ulong ulNewSize)
{
	PDEVICE_INFO *pDeviceList = NULL;

	pDeviceList = (PDEVICE_INFO *)LocalAlloc(LMEM_ZEROINIT, sizeof(g_pDeviceList) * ulNewSize);
	if ( pDeviceList )
	{
		memcpy(pDeviceList, g_pDeviceList, sizeof(g_pDeviceList) * g_dwDeviceListSize);
		LocalFree(g_pDeviceList);
		g_pDeviceList = pDeviceList;
		g_dwDeviceListSize = ulNewSize;
		
		return S_OK;
	}

	return E_OUTOFMEMORY;
}

HRESULT DeviceList::Close(void)
{
	if ( g_dwDeviceListSize && g_pDeviceList )
	{
		for (DWORD i = 0; i < g_dwDeviceListSize; ++i )
		{
			PDEVICE_INFO pDeviceInfo = g_pDeviceList[i];

			if ( pDeviceInfo )
			{
				if ( !Utilities::IsSettingSet(2) )
				{
					if ( (DriverComm::SendLEDState(pDeviceInfo, Protocol::LEDState::XINPUT_LED_OFF) & 0x80000000u) != 0 )
					{
						if ( !Utilities::IsSettingSet(4) )
						{
							pDeviceInfo->Vibration.wLeftMotorSpeed = 0;
							pDeviceInfo->Vibration.wRightMotorSpeed = 0;

							if ( (DriverComm::SendDeviceVibration(pDeviceInfo) & 0x80000000u) != 0 )
							{
								// Log failure if logging on
							}
						}
						DeviceInfo::Destroy(pDeviceInfo);
						pDeviceInfo = 0;
					}
				}
			}
		}

		LocalFree(g_pDeviceList);
		g_pDeviceList = 0;
		g_dwDeviceListSize = 0;

		return S_OK;
	}
	
	return E_FAIL;
}

HRESULT DeviceList::GetDeviceOnPort(ulong ulPort, PDEVICE_INFO *ppDeviceInfo)
{
	*ppDeviceInfo = ( ulPort < g_dwDeviceListSize ) ? g_pDeviceList[ulPort] : NULL;
	return 0;
}

HRESULT DeviceList::Initialize(void)
{
	g_dwDeviceListSize = 0;
	g_pDeviceList = (PDEVICE_INFO*)LocalAlloc(LMEM_ZEROINIT, sizeof(PDEVICE_INFO) * XINPUT_INITIAL_DEVICELIST_SIZE);

	if ( !g_pDeviceList )
		return E_FAIL;

	g_dwDeviceListSize = XINPUT_INITIAL_DEVICELIST_SIZE;
	return S_OK;	
}

HRESULT DeviceList::IsDeviceOnPort(ulong ulPort)
{
	if ( ulPort < g_dwDeviceListSize )
	{
		if ( g_pDeviceList[ulPort] )
			return S_OK;
	}
	return S_FALSE;
}                  

HRESULT DeviceList::IsDevicePresent(PDEVICE_INFO pDeviceInfo)  
{
	for ( DWORD i = 0; i < g_dwDeviceListSize; ++i )
	{
		if ( g_pDeviceList[i] )
		{
			if( pDeviceInfo->bPort == g_pDeviceList[i]->bPort && pDeviceInfo->pwchDeviceName && g_pDeviceList[i]->pwchDeviceName )
			{
				if( !wcscmp(pDeviceInfo->pwchDeviceName, g_pDeviceList[i]->pwchDeviceName) )
					return S_OK;
			}
		}
	}
	return S_FALSE;
}        

HRESULT DeviceList::RemoveDeviceFromPort(ulong ulPort)               
{
	if ( ulPort < g_dwDeviceListSize )
	{
		if ( g_pDeviceList[ulPort] )
		{
			DeviceInfo::Destroy(g_pDeviceList[ulPort]);
			g_pDeviceList[ulPort] = 0;
			return S_OK;
		}
	}

	return S_OK;
}

HRESULT DeviceList::SetDeviceOnPort(ulong ulPort, PDEVICE_INFO pDeviceInfo)    
{
	HRESULT hResult = S_OK;

	if ( ulPort >= g_dwDeviceListSize )
		hResult = GrowList(ulPort + 1);

	if( SUCCEEDED(hResult) )
	{
		if ( pDeviceInfo && g_pDeviceList[ulPort] )
			return E_FAIL;

		g_pDeviceList[ulPort] = pDeviceInfo;
		return S_OK;
	}
	
	return hResult;
}